package com.mes.cloud.mail;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.util.StringUtils;

import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class MailService {

    private static final Logger logger = LoggerFactory.getLogger(MailService.class);

    // 默认重试次数
    private static final int DEFAULT_RETRY_TIMES = 5;

    // 最大间隔时间
    private static final int MAX_PERIOD = 1 * 60 * 1000;

    // 邮件格式校验
    public static final Pattern MAIL_PATTERN = Pattern.compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$");

    // 邮件自动配置 邮件发送工厂
    private MailAutoConfiguration.MailSenderFactory mailSenderFactory;

    public MailService(MailAutoConfiguration.MailSenderFactory mailSenderFactory) {
        this.mailSenderFactory = mailSenderFactory;
    }

    /**
     * 发送邮件.
     * <p>
     * 如果失败，会以随机间隔时间重试5次，调用者应确保在异步事件中调用此接口，或不会阻塞业务逻辑的运行。
     *
     * @param mail 要发送的邮件
     * @return 成功与否
     */
    public boolean sendMail(final Mail mail) {
        return sendMail(null, mail, DEFAULT_RETRY_TIMES);
    }

    /**
     * 发送邮件.
     * <p>
     * 只会尝试发送一次。
     *
     * @param mail 要发送的邮件
     * @return 成功与否
     */
    public boolean sendMailOnce(final Mail mail) {
        return sendMail(null, mail, 0);
    }


    public boolean sendMail(String senderName, final Mail mail, int retryTimes) {
        if (!validateEmail(mail.getTo())) {
            logger.error("邮箱地址错误，邮件发送失败. {}", mail);
            return false;
        }
        MailAutoConfiguration.MailSender sender = null;
        try {
            sender = mailSenderFactory.getSender(senderName);
        } catch (Throwable e) {
            logger.error("获取邮件发送器错误. {}", mail, e);
        }
        if (sender == null) return false;


        MimeMessage message = null;
        try {
            message = createMessage(sender, mail.getTo(), mail.getSubject(), mail.getFrom(),
                    mail.getContent(), mail.getAttachments(), mail.getInlines());
        } catch (Throwable e) {
            logger.error("构造邮件失败. {}", mail, e);
        }
        if (message == null) return false;


        SecureRandom random = new SecureRandom();

        SendResult result = doSend(sender, message);
        while (!result.isSucceed() && (retryTimes-- > 0)) {
            try {
                Thread.sleep(random.nextInt(MAX_PERIOD));
            } catch (Throwable e) {
                //ignored
            }
            result = doSend(sender, message);
        }

        if (result.isSucceed()) {
            logger.debug("邮件发送成功. {}", mail);
            return true;
        }

        logger.error("邮件发送失败. {}", mail, result.getThrowable());
        return false;
    }

    private SendResult doSend(MailAutoConfiguration.MailSender sender, MimeMessage message) {
        try {
            sender.send(message);
            return SendResult.success();
        } catch (Throwable e) {
            return SendResult.fail(e);
        }
    }

    @Data
    private static class SendResult {
        private boolean succeed;
        private Throwable throwable;

        public static SendResult success() {
            SendResult result = new SendResult();
            result.setSucceed(true);
            return result;
        }

        public static SendResult fail(Throwable e) {
            SendResult result = new SendResult();
            result.setSucceed(false);
            result.setThrowable(e);
            return result;
        }
    }


    private MimeMessage createMessage(MailAutoConfiguration.MailSender sender, String to, String subject, String cc,
                                      String content, Map<String, DataSource> attachments, Map<String, DataSource> inlines)
            throws MessagingException {

        MimeMessage msg = sender.createMimeMessage();

        boolean multipart = false;
        if (!attachments.isEmpty() || !inlines.isEmpty()) {
            multipart = true;
        }

        MimeMessageHelper helper = new MimeMessageHelper(msg, multipart,
                MailAutoConfiguration.MailSenderFactory.DEFAULT_ENCODING);

        helper.setTo(StringUtils.tokenizeToStringArray(to, ","));
        /*try {
            helper.setFrom(sender.getUsername(), sender.getFrom());
        } catch (UnsupportedEncodingException e) {
            helper.setFrom(sender.getUsername());
        }*/
        msg.setFrom(sender.getFrom());

        helper.setSubject(subject);

        if (!StringUtils.isEmpty(cc)) {
            helper.setCc(StringUtils.tokenizeToStringArray(cc, ","));
        }

        helper.setText(content, true);
        for (Entry<String, DataSource> entry : attachments.entrySet()) {
            helper.addAttachment(entry.getKey(), entry.getValue());
        }

        for (Entry<String, DataSource> entry : inlines.entrySet()) {
            helper.addInline(entry.getKey(), entry.getValue());
        }

        return msg;
    }

    /**
     * 校验邮件是否为空和格式是否正确
     * @param email
     * @return
     */
    public static boolean validateEmail(String email) {
        if (StringUtils.isEmpty(email)) {
            return false;
        }
        Matcher m = MAIL_PATTERN.matcher(email);
        return m.matches();
    }

}
